namespace Sudoku.SourceGeneration.Handlers;

internal static class TypeImplHandler
{
	private const string IsLargeStructurePropertyName = "IsLargeStructure";

	private const string OtherModifiersOnEqualsPropertyName = "OtherModifiersOnEquals";

	private const string OtherModifiersOnToStringPropertyName = "OtherModifiersOnToString";

	private const string EqualsBehaviorPropertyName = "EqualsBehavior";

	private const string ToStringBehaviorPropertyName = "ToStringBehavior";

	private const string GetHashCodeBehaviorPropertyName = "GetHashCodeBehavior";

	private const string OperandNullabilityPreferPropertyName = "OperandNullabilityPrefer";


	public static List<string>? Transform(GeneratorAttributeSyntaxContext gasc, CancellationToken cancellationToken)
	{
		var typeSources = new List<string>();
		if (Object_Equals(gasc) is { } source1)
		{
			typeSources.Add(source1);
		}
		if (Object_GetHashCode(gasc, cancellationToken) is { } source2)
		{
			typeSources.Add(source2);
		}
		if (Object_ToString(gasc, cancellationToken) is { } source3)
		{
			typeSources.Add(source3);
		}
		if (EqualityOperators(gasc) is { } source4)
		{
			typeSources.Add(source4);
		}
		if (ComparisonOperators(gasc) is { } source5)
		{
			typeSources.Add(source5);
		}
		return typeSources;
	}

	public static void Output(SourceProductionContext spc, ImmutableArray<List<string>> value)
		=> spc.AddSource(
			"TypeImpl.g.cs",
			$"""
			{Banner.AutoGenerated}

			#nullable enable
			
			{string.Join("\r\n\r\n", from element in value from nested in element select nested)}
			"""
		);

	private static string? Object_Equals(GeneratorAttributeSyntaxContext gasc)
	{
		if (gasc is not
			{
				Attributes: [{ ConstructorArguments: [{ Value: int ctorArg }] } attribute],
				TargetSymbol: INamedTypeSymbol
				{
					TypeKind: var kind and (TypeKind.Struct or TypeKind.Class),
					Name: var typeName,
					IsRecord: false, // Records cannot manually overrides 'Equals' method.
					IsReadOnly: var isReadOnly,
					IsRefLikeType: var isRefStruct,
					TypeParameters: var typeParameters,
					ContainingNamespace: var @namespace,
					ContainingType: null // Must be top-level type.
				} type,
				SemanticModel.Compilation: var compilation
			})
		{
			return null;
		}

		if (!((TypeImplFlag)ctorArg).HasFlag(TypeImplFlag.Object_Equals))
		{
			return null;
		}

		var isLargeStructure = attribute.GetNamedArgument(IsLargeStructurePropertyName, false);
		var namespaceString = @namespace.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)["global::".Length..];
		var behavior = attribute.GetNamedArgument(EqualsBehaviorPropertyName, 0) switch
		{
			0 => (isRefStruct, kind) switch
			{
				(true, _) => EqualsBehavior.ReturnFalse,
				(_, TypeKind.Struct) => EqualsBehavior.IsCast,
				(_, TypeKind.Class) => EqualsBehavior.AsCast,
				_ => throw new InvalidOperationException("Invalid state.")
			},
			1 => EqualsBehavior.Throw,
			2 => EqualsBehavior.MakeAbstract,
			_ => throw new InvalidOperationException("Invalid state.")
		};
		var otherModifiers = attribute.GetNamedArgument<string>(OtherModifiersOnEqualsPropertyName) switch
		{
			{ } str => str.Split([' '], StringSplitOptions.RemoveEmptyEntries),
			_ => []
		};
		var typeArgumentsString = typeParameters is []
			? string.Empty
			: $"<{string.Join(", ", from typeParameter in typeParameters select typeParameter.Name)}>";
		var typeNameString = $"{typeName}{typeArgumentsString}";
		var fullTypeNameString = $"global::{namespaceString}.{typeNameString}";
		var typeKindString = kind switch
		{
			TypeKind.Class => "class",
			TypeKind.Struct => "struct",
			_ => throw new InvalidOperationException("Invalid state.")
		};
		var otherModifiersString = otherModifiers.Length == 0 ? string.Empty : $"{string.Join(" ", otherModifiers)} ";
		if (behavior == EqualsBehavior.MakeAbstract)
		{
			return $$"""
				namespace {{namespaceString}}
				{
				#line 1 "TypeImpl.{{typeNameString}}_Equals.g.cs"
					partial {{typeKindString}} {{typeNameString}}
					{
						/// <inheritdoc cref="object.Equals(object?)"/>
						public {{otherModifiersString}}abstract override bool Equals([global::System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj);
					}
				#line default
				}
				""";
		}
		else
		{
			var inKeyword = isLargeStructure ? "in " : string.Empty;
			var expressionString = behavior switch
			{
				EqualsBehavior.ReturnFalse => "false",
				EqualsBehavior.IsCast => $"obj is {fullTypeNameString} comparer && Equals({inKeyword}comparer)",
				EqualsBehavior.AsCast => $"Equals(obj as {fullTypeNameString})",
				EqualsBehavior.Throw => """throw new global::System.NotSupportedException("This method is not supported or disallowed by author.")""",
				_ => throw new InvalidOperationException("Invalid state.")
			};
			var attributesMarked = isRefStruct
				? behavior == EqualsBehavior.ReturnFalse
					? """
					[global::System.ObsoleteAttribute("Calling this method is unexpected because author disallow you call this method on purpose.", true)]
					"""
					: """
					[global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
							[global::System.ObsoleteAttribute("Calling this method is unexpected because author disallow you call this method on purpose.", true)]
					"""
				: """
				[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
				""";
			var readOnlyModifier = kind == TypeKind.Struct && !isReadOnly ? "readonly " : string.Empty;
			var isDeprecated = attributesMarked.Contains("ObsoleteAttribute");
			var suppress0809 = isDeprecated ? "#pragma warning disable CS0809\r\n\t" : "\t";
			var enable0809 = isDeprecated ? "#pragma warning restore CS0809\r\n\t" : string.Empty;
			return $$"""
				namespace {{namespaceString}}
				{
				#line 1 "TypeImpl.{{typeNameString}}_Equals.g.cs"
				{{suppress0809}}partial {{typeKindString}} {{typeNameString}}
					{
						/// <inheritdoc cref="object.Equals(object?)"/>
						{{attributesMarked}}
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						public {{otherModifiersString}}override {{readOnlyModifier}}bool Equals([global::System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj)
							=> {{expressionString}};
					}
				#line default
				{{enable0809}}}
				""";
		}
	}

	private static string? Object_GetHashCode(GeneratorAttributeSyntaxContext gasc, CancellationToken cancellationToken)
	{
		if (gasc is not
			{
				Attributes: [{ ConstructorArguments: [{ Value: int ctorArg }] } attribute],
				TargetSymbol: INamedTypeSymbol
				{
					Name: var typeName,
					ContainingNamespace: var @namespace,
					TypeParameters: var typeParameters,
					TypeKind: var kind and (TypeKind.Class or TypeKind.Struct),
					IsRecord: var isRecord,
					IsReadOnly: var isReadOnly,
					IsRefLikeType: var isRefStruct,
					ContainingType: null
				} type,
				TargetNode: TypeDeclarationSyntax { ParameterList: var parameterList }
					and (RecordDeclarationSyntax or ClassDeclarationSyntax or StructDeclarationSyntax),
				SemanticModel: { Compilation: var compilation } semanticModel
			})
		{
			return null;
		}

		if (!((TypeImplFlag)ctorArg).HasFlag(TypeImplFlag.Object_GetHashCode))
		{
			return null;
		}

		var namespaceString = @namespace.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)["global::".Length..];
		var typeParametersString = typeParameters is []
			? string.Empty
			: $"<{string.Join(", ", from typeParameter in typeParameters select typeParameter.Name)}>";
		var typeNameString = $"{typeName}{typeParametersString}";

		const string dataMemberAttributeTypeName = "System.Diagnostics.CodeAnalysis.PrimaryConstructorParameterAttribute";
		var dataMemberAttributeTypeNameSymbol = compilation.GetTypeByMetadataName(dataMemberAttributeTypeName);
		if (dataMemberAttributeTypeNameSymbol is null)
		{
			return null;
		}

		const string hashCodeMemberAttributeTypeName = "System.Diagnostics.CodeAnalysis.HashCodeMemberAttribute";
		var hashCodeMemberAttributeSymbol = compilation.GetTypeByMetadataName(hashCodeMemberAttributeTypeName);
		if (hashCodeMemberAttributeSymbol is null)
		{
			return null;
		}

		var referencedMembers = PrimaryConstructor.GetCorrespondingMemberNames(
			type,
			semanticModel,
			parameterList,
			dataMemberAttributeTypeNameSymbol,
			a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, hashCodeMemberAttributeSymbol),
			static symbol => symbol switch
			{
				IFieldSymbol { Type.SpecialType: System_Byte or System_SByte or System_Int16 or System_UInt16 or System_Int32 } => true,
				IFieldSymbol { Type.TypeKind: TypeKind.Enum } => false,
				IPropertySymbol { Type.SpecialType: System_Byte or System_SByte or System_Int16 or System_UInt16 or System_Int32 } => true,
				IPropertySymbol { Type.TypeKind: TypeKind.Enum } => false,
				IParameterSymbol { Type.SpecialType: System_Byte or System_SByte or System_Int16 or System_UInt16 or System_Int32 } => true,
				IParameterSymbol { Type.TypeKind: TypeKind.Enum } => false,
				_ => default(bool?)
			},
			cancellationToken
		);

#pragma warning disable format
		var behavior = (isRefStruct, attribute) switch
		{
			(true, _) => GetHashCodeBehavior.Throw,
			_ => attribute.GetNamedArgument<int>(GetHashCodeBehaviorPropertyName) switch
			{
				0 => referencedMembers switch
				{
					[] => GetHashCodeBehavior.ReturnNegativeOne,
					[(_, true)] => GetHashCodeBehavior.Direct,
					[(_, false)] => GetHashCodeBehavior.EnumExplicitCast,
					{ Length: > 8 } => GetHashCodeBehavior.HashCodeAdd,
					_ => GetHashCodeBehavior.Specified
				},
				1 => GetHashCodeBehavior.Throw,
				2 => GetHashCodeBehavior.MakeAbstract,
				_ => throw new InvalidOperationException("Invalid state.")
			}
		};
#pragma warning restore format
		var kindString = (isRecord, kind) switch
		{
			(true, TypeKind.Class) => "record",
			(true, TypeKind.Struct) => "record struct",
			(_, TypeKind.Class) => "class",
			(_, TypeKind.Struct) => "struct",
			_ => throw new InvalidOperationException("Invalid state.")
		};
		var otherModifiers = attribute.GetNamedArgument<string>("OtherModifiersOnGetHashCode") switch
		{
			{ } str => str.Split([' '], StringSplitOptions.RemoveEmptyEntries),
			_ => []
		};
		var otherModifiersString = otherModifiers.Length == 0 ? string.Empty : $"{string.Join(" ", otherModifiers)} ";
		if (behavior == GetHashCodeBehavior.MakeAbstract)
		{
			return $$"""
				namespace {{namespaceString}}
				{
				#line 1 "TypeImpl.{{typeNameString}}_GetHashCode.g.cs"
					partial {{kindString}} {{typeNameString}}
					{
						/// <inheritdoc cref="object.GetHashCode"/>
						public {{otherModifiersString}}abstract override int GetHashCode();
					}
				#line default
				}
				""";
		}
		else
		{
			var codeBlock = behavior switch
			{
				GetHashCodeBehavior.ReturnNegativeOne => @"	=> -1;",
				GetHashCodeBehavior.Direct => $@"	=> {referencedMembers[0].Name};",
				GetHashCodeBehavior.EnumExplicitCast => $@"	=> (int){referencedMembers[0].Name};",
				GetHashCodeBehavior.Specified
					=> $@"	=> global::System.HashCode.Combine({string.Join(", ", from pair in referencedMembers select pair.Name)});",
				GetHashCodeBehavior.Throw
					=> @"	=> throw new global::System.NotSupportedException(""This method is not supported or disallowed by author."");",
				GetHashCodeBehavior.HashCodeAdd
					=> $$"""
						{
							var hashCode = new global::System.HashCode();
							{{string.Join("\r\n\r\n", from member in referencedMembers select $"hashCode.Add({member.Name});")}}
							return hashCode.ToHashCode();
						}
					""",
				_ => throw new InvalidOperationException("Invalid state.")
			};
			var attributesMarked = (isRefStruct, behavior) switch
			{
				(true, not GetHashCodeBehavior.ReturnNegativeOne) or (_, GetHashCodeBehavior.Throw)
					=> """
					[global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
							[global::System.ObsoleteAttribute("Calling this method is unexpected because author disallow you call this method on purpose.", true)]
					""",
				(true, _)
					=> """
					[global::System.ObsoleteAttribute("Calling this method is unexpected because author disallow you call this method on purpose.", true)]
					""",
				_
					=> """
					[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
					"""
			};
			var readOnlyModifier = kind == TypeKind.Struct && !isReadOnly ? "readonly " : string.Empty;
			var isDeprecated = attributesMarked.Contains("ObsoleteAttribute");
			var suppress0809 = isDeprecated
				? "#pragma warning disable CS0809\r\n\t"
				: "\t";
			var enable0809 = isDeprecated
				? "#pragma warning restore CS0809\r\n\t"
				: "\t";
			return $$"""
				namespace {{namespaceString}}
				{
				#line 1 "TypeImpl.{{typeNameString}}_GetHashCode.g.cs"
				{{suppress0809}}partial {{kindString}} {{typeNameString}}
					{
						/// <inheritdoc cref="object.GetHashCode"/>
						{{attributesMarked}}
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						public {{otherModifiersString}}override {{readOnlyModifier}}int GetHashCode()
						{{codeBlock}}
				#line default
				{{enable0809}}}
				}
				""";
		}
	}

	private static string? Object_ToString(GeneratorAttributeSyntaxContext gasc, CancellationToken cancellationToken)
	{
		if (gasc is not
			{
				Attributes: [{ ConstructorArguments: [{ Value: int ctorArg }] } attribute],
				TargetSymbol: INamedTypeSymbol
				{
					Name: var typeName,
					ContainingNamespace: var @namespace,
					TypeParameters: var typeParameters,
					TypeKind: var kind and (TypeKind.Class or TypeKind.Struct),
					IsRecord: var isRecord,
					IsReadOnly: var isReadOnly,
					IsRefLikeType: var isRefStruct,
					ContainingType: null
				} type,
				TargetNode: TypeDeclarationSyntax { ParameterList: var parameterList }
					and (RecordDeclarationSyntax or ClassDeclarationSyntax or StructDeclarationSyntax),
				SemanticModel: { Compilation: var compilation } semanticModel
			})
		{
			return null;
		}

		if (!((TypeImplFlag)ctorArg).HasFlag(TypeImplFlag.Object_ToString))
		{
			return null;
		}

		var namespaceString = @namespace.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)["global::".Length..];
		var typeParametersString = typeParameters is []
			? string.Empty
			: $"<{string.Join(", ", from typeParameter in typeParameters select typeParameter.Name)}>";
		var typeNameString = $"{typeName}{typeParametersString}";
		var fullTypeNameString = $"global::{namespaceString}.{typeNameString}";
		const string formattableTypeName = "System.IFormattable";
		if (compilation.GetTypeByMetadataName(formattableTypeName) is not { } formattableTypeSymbol)
		{
			return null;
		}

		const string dataMemberAttributeTypeName = "System.Diagnostics.CodeAnalysis.PrimaryConstructorParameterAttribute";
		var dataMemberAttributeTypeNameSymbol = compilation.GetTypeByMetadataName(dataMemberAttributeTypeName);
		if (dataMemberAttributeTypeNameSymbol is null)
		{
			return null;
		}

		const string stringMemberAttributeName = "System.Diagnostics.CodeAnalysis.StringMemberAttribute";
		var stringMemberAttributeSymbol = compilation.GetTypeByMetadataName(stringMemberAttributeName);
		if (stringMemberAttributeSymbol is null)
		{
			return null;
		}

		const string formatProviderTypeName = "System.IFormatProvider";
		var formatProviderSymbol = compilation.GetTypeByMetadataName(formatProviderTypeName);
		if (formatProviderSymbol is null)
		{
			return null;
		}

		var referencedMembers = PrimaryConstructor.GetCorrespondingMemberNames(
			type,
			semanticModel,
			parameterList,
			dataMemberAttributeTypeNameSymbol,
			a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, stringMemberAttributeSymbol),
			symbol => (string?)symbol.GetAttributes().First(stringMemberAttirbuteMatcher).ConstructorArguments[0].Value ?? symbol.Name,
			cancellationToken
		);

		var behavior = attribute.GetNamedArgument<int>(ToStringBehaviorPropertyName) switch
		{
			0 => (isRefStruct, referencedMembers) switch
			{
				(true, _) => ToStringBehavior.Throw,
				_ when hasImpledFormattable(type) is var p and not null => p switch
				{
					true => ToStringBehavior.CallOverloadImplicit,
					_ => ToStringBehavior.CallOverloadExplicit
				},
				(_, []) => ToStringBehavior.RecordLike,
				(_, { Length: 1 }) => ToStringBehavior.Specified,
				_ => ToStringBehavior.RecordLike
			},
			1 => ToStringBehavior.CallOverloadExplicit,
			2 when referencedMembers.Length == 1 => ToStringBehavior.Specified,
			3 when referencedMembers.Length != 0 => ToStringBehavior.RecordLike,
			4 => ToStringBehavior.Throw,
			5 => ToStringBehavior.MakeAbstract,
			_ => ToStringBehavior.ReturnTypeName
		};
		var kindString = (isRecord, kind) switch
		{
			(true, TypeKind.Class) => "record",
			(true, TypeKind.Struct) => "record struct",
			(_, TypeKind.Class) => "class",
			(_, TypeKind.Struct) => "struct",
			_ => throw new InvalidOperationException("Invalid state.")
		};
		var otherModifiers = attribute.GetNamedArgument<string>(OtherModifiersOnToStringPropertyName) switch
		{
			{ } str => str.Split([' '], StringSplitOptions.RemoveEmptyEntries),
			_ => []
		};
		var otherModifiersString = otherModifiers.Length == 0 ? string.Empty : $"{string.Join(" ", otherModifiers)} ";
		if (behavior == ToStringBehavior.MakeAbstract)
		{
			return $$"""
				namespace {{namespaceString}}
				{
				#line 1 "TypeImpl.{{typeNameString}}_ToString.g.cs"
					partial {{kindString}} {{typeNameString}}
					{
						/// <inheritdoc cref="object.ToString"/>
						public {{otherModifiersString}}abstract override string ToString();
					}
				#line default
				}
				""";
		}
		else
		{
			var expression = behavior switch
			{
				ToStringBehavior.ReturnTypeName => fullTypeNameString,
				ToStringBehavior.CallOverloadImplicit => "ToString(default(string), default(global::System.IFormatProvider))",
				ToStringBehavior.CallOverloadExplicit => "((global::System.IFormattable)this).ToString(null, null)",
				ToStringBehavior.Specified => referencedMembers[0].Name,
				ToStringBehavior.Throw => """throw new global::System.NotSupportedException("This method is not supported or disallowed by author.")""",
				ToStringBehavior.RecordLike
					=> $$$"""
					$"{{{typeName}}} {{ {{{string.Join(", ", f(referencedMembers))}}} }}"
					""",
				_ => throw new InvalidOperationException("Invalid state.")
			};

			var attributesMarked = isRefStruct && behavior is ToStringBehavior.Throw or ToStringBehavior.ReturnTypeName
				? behavior == ToStringBehavior.ReturnTypeName
					? """
					[global::System.ObsoleteAttribute("Calling this method is unexpected because author disallow you call this method on purpose.", true)]
					"""
					: """
					[global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute]
							[global::System.ObsoleteAttribute("Calling this method is unexpected because author disallow you call this method on purpose.", true)]
					"""
				: """
				[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
				""";
			var readOnlyModifier = kind == TypeKind.Struct && !isReadOnly ? "readonly " : string.Empty;
			var isDeprecated = attributesMarked.Contains("ObsoleteAttribute");
			var suppress0809 = isDeprecated ? "#pragma warning disable CS0809\r\n\t" : "\t";
			var enable0809 = isDeprecated ? "#pragma warning restore CS0809\r\n\t" : "\t";
			return $$"""
				namespace {{namespaceString}}
				{
				#line 1 "TypeImpl.{{typeNameString}}_ToString.g.cs"
				{{suppress0809}}partial {{kindString}} {{typeNameString}}
					{
						/// <inheritdoc cref="object.ToString"/>
						{{attributesMarked}}
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						public {{otherModifiersString}}override {{readOnlyModifier}}string ToString()
							=> {{expression}};
				#line default
				{{enable0809}}}
				}
				""";
		}


		static IEnumerable<string> f((string Name, string ExtraData)[] referencedMembers)
			=>
			from referencedMember in referencedMembers
			let displayName = referencedMember.ExtraData
			let name = referencedMember.Name
			select $$"""{{displayName ?? $$"""{nameof({{name}})}"""}} = {{{name}}}""";

		bool? hasImpledFormattable(INamedTypeSymbol type)
		{
			if (type.Interfaces.Contains(formattableTypeSymbol, SymbolEqualityComparer.Default))
			{
				// Extra check: check wheteher the method is explicitly-impl'ed. If so, we should return 'false'.
#pragma warning disable format
				return type.GetMembers().OfType<IMethodSymbol>().Any(
					method => method is
					{
						Name: "ToString",
						IsImplicitlyDeclared: false,
						ExplicitInterfaceImplementations: not [],
						TypeParameters: [],
						Parameters: [
							{
								Type.SpecialType: System_String,
								NullableAnnotation: not NotAnnotated
							},
							{
								Type: var formatProviderType
							}
						],
						ReturnType.SpecialType: System_String
					} && SymbolEqualityComparer.IncludeNullability.Equals(formatProviderType, formatProviderSymbol)
				);
#pragma warning restore format
			}

			return type.AllInterfaces.Contains(formattableTypeSymbol, SymbolEqualityComparer.Default) ? false : null;
		}

		bool stringMemberAttirbuteMatcher(AttributeData a)
			=> SymbolEqualityComparer.Default.Equals(a.AttributeClass, stringMemberAttributeSymbol);
	}

	private static string? EqualityOperators(GeneratorAttributeSyntaxContext gasc)
	{
		if (gasc is not
			{
				Attributes: [{ ConstructorArguments: [{ Value: int ctorArg }] } attribute],
				TargetSymbol: INamedTypeSymbol
				{
					Name: var typeName,
					ContainingNamespace: var @namespace,
					ContainingType: null,
					IsRecord: var isRecord,
					TypeKind: var kind and (TypeKind.Class or TypeKind.Struct or TypeKind.Interface),
					TypeParameters: var typeParameters,
					IsRefLikeType: var isRefStruct
				} type,
				SemanticModel.Compilation: var compilation
			})
		{
			return null;
		}

		if (!((TypeImplFlag)ctorArg).HasFlag(TypeImplFlag.EqualityOperators))
		{
			return null;
		}

		if (kind == TypeKind.Interface && (
			typeParameters is not [{ ConstraintTypes: var constraintTypes }, ..]
			|| !constraintTypes.Contains(type, SymbolEqualityComparer.Default)
		))
		{
			// If the type is an interface, we should check for whether its first type parameter is a self type parameter,
			// which means it should implement its containing interface type.
			return null;
		}

		var isLargeStructure = attribute.GetNamedArgument<bool>(IsLargeStructurePropertyName);
		var behavior = (isRecord, kind, isLargeStructure) switch
		{
			(true, TypeKind.Class, _) => EqualityOperatorsBehavior.DoNothing,
			(_, TypeKind.Class, _) => EqualityOperatorsBehavior.Default,
			(true, TypeKind.Struct, true) => EqualityOperatorsBehavior.WithScopedInButDeprecated,
			(true, TypeKind.Struct, _) => EqualityOperatorsBehavior.DefaultButDeprecated,
			(_, TypeKind.Struct, true) => EqualityOperatorsBehavior.WithScopedIn,
			(_, TypeKind.Struct, _) => EqualityOperatorsBehavior.Default,
			_ => throw new InvalidOperationException("Invalid state.")
		};
		if (behavior == EqualityOperatorsBehavior.DoNothing)
		{
			return null;
		}

		var typeKindString = (isRecord, kind) switch
		{
			(true, TypeKind.Class) => "record",
			(_, TypeKind.Class) => "class",
			(true, TypeKind.Struct) => "record struct",
			(_, TypeKind.Struct) => "struct",
			(_, TypeKind.Interface) => "interface",
			_ => throw new InvalidOperationException("Invalid state.")
		};
		var namespaceString = @namespace.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)["global::".Length..];
		var typeArgumentsString = typeParameters is []
			? string.Empty
			: $"<{string.Join(", ", from typeParameter in typeParameters select typeParameter.Name)}>";
		var typeNameString = $"{typeName}{typeArgumentsString}";
		var fullTypeNameString = $"global::{namespaceString}.{typeNameString}";
		const string nullableToken = "?";
		var nullabilityToken = (attribute.GetNamedArgument<int>(OperandNullabilityPreferPropertyName), kind) switch
		{
			(0, TypeKind.Class) or (2, _) => nullableToken,
			(0, TypeKind.Struct) or (1, _) => string.Empty,
			(0, TypeKind.Interface) => typeParameters[0] switch
			{
				{ HasNotNullConstraint: true } => nullableToken, // Unknown T.
				{ HasUnmanagedTypeConstraint: true } or { HasValueTypeConstraint: true } => string.Empty,
				{ HasReferenceTypeConstraint: true } => nullableToken,
				{ ReferenceTypeConstraintNullableAnnotation: Annotated } => nullableToken, // Reference type inferred.
				{ ConstraintNullableAnnotations: var annotations } when annotations.Contains(Annotated) => nullableToken, // Reference type inferred.
				_ => string.Empty
			},
			_ => throw new InvalidOperationException("Invalid state.")
		};
		var attributesMarked = behavior switch
		{
			EqualityOperatorsBehavior.WithScopedInButDeprecated or EqualityOperatorsBehavior.DefaultButDeprecated
				=> """
				[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
						[global::System.ObsoleteAttribute("This operator is not recommended to be defined in a record struct, because it'll be auto-generated a pair of equality operators by compiler, without any modifiers modified two parameters.", false)]
				""",
			_
				=> """
				[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
				"""
		};
		var inKeyword = isLargeStructure ? "in " : string.Empty;
		var (i1, i2) = nullabilityToken switch
		{
			nullableToken => (
				$$"""(left, right) switch { (null, null) => true, ({ } a, { } b) => a.Equals({{inKeyword}}b), _ => false }""",
				"!(left == right)"
			),
			_ => ($"left.Equals({inKeyword}right)", $"!(left == right)")
		};

		var explicitImplementation = string.Empty;
		var equalityOperatorsType = compilation.GetTypeByMetadataName("System.Numerics.IEqualityOperators`3")!
			.Construct(type, type, compilation.GetSpecialType(System_Boolean));
		if (behavior is EqualityOperatorsBehavior.WithScopedIn or EqualityOperatorsBehavior.WithScopedInButDeprecated
			&& type.AllInterfaces.Contains(equalityOperatorsType, SymbolEqualityComparer.Default))
		{
			explicitImplementation =
				$$"""
				/// <inheritdoc/>
						[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						static bool global::System.Numerics.IEqualityOperators<{{fullTypeNameString}}, {{fullTypeNameString}}, bool>.operator ==({{fullTypeNameString}} left, {{fullTypeNameString}} right) => left == right;

						/// <inheritdoc/>
						[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						static bool global::System.Numerics.IEqualityOperators<{{fullTypeNameString}}, {{fullTypeNameString}}, bool>.operator !=({{fullTypeNameString}} left, {{fullTypeNameString}} right) => left != right;
				""";
		}

		var operatorDeclaration = behavior switch
		{
			EqualityOperatorsBehavior.Default or EqualityOperatorsBehavior.DefaultButDeprecated
				=> $$"""
				/// <inheritdoc cref="global::System.Numerics.IEqualityOperators{TSelf, TOther, TResult}.op_Equality(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator ==({{fullTypeNameString}}{{nullabilityToken}} left, {{fullTypeNameString}}{{nullabilityToken}} right)
							=> {{i1}};

						/// <inheritdoc cref="global::System.Numerics.IEqualityOperators{TSelf, TOther, TResult}.op_Inequality(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator !=({{fullTypeNameString}}{{nullabilityToken}} left, {{fullTypeNameString}}{{nullabilityToken}} right)
							=> {{i2}};
				""",
			EqualityOperatorsBehavior.WithScopedIn or EqualityOperatorsBehavior.WithScopedInButDeprecated
				=> $$"""
				/// <inheritdoc cref="global::System.Numerics.IEqualityOperators{TSelf, TOther, TResult}.op_Equality(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator ==(in {{fullTypeNameString}}{{nullabilityToken}} left, in {{fullTypeNameString}}{{nullabilityToken}} right)
							=> {{i1}};

						/// <inheritdoc cref="global::System.Numerics.IEqualityOperators{TSelf, TOther, TResult}.op_Inequality(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator !=(in {{fullTypeNameString}}{{nullabilityToken}} left, in {{fullTypeNameString}}{{nullabilityToken}} right)
							=> {{i2}};

						{{explicitImplementation}}
				""",
			_ => null
		};
		if (operatorDeclaration is null)
		{
			return null;
		}

		return $$"""
			namespace {{namespaceString}}
			{
			#line 1 "TypeImpl.{{typeNameString}}_EqualityOperators.g.cs"
				partial {{typeKindString}} {{typeNameString}}
				{
					{{operatorDeclaration}}
				}
			#line default
			}
			""";
	}

	private static string? ComparisonOperators(GeneratorAttributeSyntaxContext gasc)
	{
		if (gasc is not
			{
				Attributes: [{ ConstructorArguments: [{ Value: int ctorArg }] } attribute],
				TargetSymbol: INamedTypeSymbol
				{
					Name: var typeName,
					ContainingNamespace: var @namespace,
					ContainingType: null,
					IsRecord: var isRecord,
					TypeKind: var kind and (TypeKind.Class or TypeKind.Struct or TypeKind.Interface),
					TypeParameters: var typeParameters,
					IsRefLikeType: var isRefStruct
				} type,
				SemanticModel.Compilation: var compilation
			})
		{
			return null;
		}

		if (!((TypeImplFlag)ctorArg).HasFlag(TypeImplFlag.ComparisonOperators))
		{
			return null;
		}

		var isLargeStructure = attribute.GetNamedArgument<bool>(IsLargeStructurePropertyName);
		var behavior = (isRecord, kind, isLargeStructure) switch
		{
			(true, TypeKind.Class, _) => ComparisonOperatorsBehavior.DoNothing,
			(_, TypeKind.Class, _) => ComparisonOperatorsBehavior.Default,
			(true, TypeKind.Struct, true) => ComparisonOperatorsBehavior.WithScopedInButDeprecated,
			(true, TypeKind.Struct, _) => ComparisonOperatorsBehavior.DefaultButDeprecated,
			(_, TypeKind.Struct, true) => ComparisonOperatorsBehavior.WithScopedIn,
			(_, TypeKind.Struct, _) => ComparisonOperatorsBehavior.Default,
			_ => throw new InvalidOperationException("Invalid state.")
		};
		if (behavior == ComparisonOperatorsBehavior.DoNothing)
		{
			return null;
		}

		var typeKindString = (isRecord, kind) switch
		{
			(true, TypeKind.Class) => "record",
			(_, TypeKind.Class) => "class",
			(true, TypeKind.Struct) => "record struct",
			(_, TypeKind.Struct) => "struct",
			_ => throw new InvalidOperationException("Invalid state.")
		};
		var namespaceString = @namespace.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)["global::".Length..];
		var typeArgumentsString = typeParameters is []
			? string.Empty
			: $"<{string.Join(", ", from typeParameter in typeParameters select typeParameter.Name)}>";
		var typeNameString = $"{typeName}{typeArgumentsString}";
		var fullTypeNameString = $"global::{namespaceString}.{typeNameString}";
		var attributesMarked = behavior switch
		{
			ComparisonOperatorsBehavior.WithScopedInButDeprecated or ComparisonOperatorsBehavior.DefaultButDeprecated
				=> """
				[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
						[global::System.ObsoleteAttribute("This operator is not recommended to be defined in a record struct, because it'll be auto-generated a pair of equality operators by compiler, without any modifiers modified two parameters.", false)]
				""",
			_
				=> """
				[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
				"""
		};
		var inKeyword = isLargeStructure ? "in " : string.Empty;
		var (i1, i2, i3, i4) = (
			$"left.CompareTo({inKeyword}right) > 0",
			$"left.CompareTo({inKeyword}right) < 0",
			$"left.CompareTo({inKeyword}right) >= 0",
			$"left.CompareTo({inKeyword}right) <= 0"
		);

		var explicitImplementation = string.Empty;
		var equalityOperatorsType = compilation.GetTypeByMetadataName("System.Numerics.IComparisonOperators`3")!
			.Construct(type, type, compilation.GetSpecialType(System_Boolean));
		if (behavior is ComparisonOperatorsBehavior.WithScopedIn or ComparisonOperatorsBehavior.WithScopedInButDeprecated
			&& type.AllInterfaces.Contains(equalityOperatorsType, SymbolEqualityComparer.Default))
		{
			explicitImplementation =
				$$"""
				/// <inheritdoc/>
						[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						static bool global::System.Numerics.IComparisonOperators<{{fullTypeNameString}}, {{fullTypeNameString}}, bool>.operator >({{fullTypeNameString}} left, {{fullTypeNameString}} right) => left > right;

						/// <inheritdoc/>
						[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						static bool global::System.Numerics.IComparisonOperators<{{fullTypeNameString}}, {{fullTypeNameString}}, bool>.operator <({{fullTypeNameString}} left, {{fullTypeNameString}} right) => left < right;

						[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						static bool global::System.Numerics.IComparisonOperators<{{fullTypeNameString}}, {{fullTypeNameString}}, bool>.operator >=({{fullTypeNameString}} left, {{fullTypeNameString}} right) => left >= right;

						/// <inheritdoc/>
						[global::System.Runtime.CompilerServices.MethodImplAttribute(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						static bool global::System.Numerics.IComparisonOperators<{{fullTypeNameString}}, {{fullTypeNameString}}, bool>.operator <=({{fullTypeNameString}} left, {{fullTypeNameString}} right) => left <= right;
				""";
		}

		var operatorDeclaration = behavior switch
		{
			ComparisonOperatorsBehavior.Default or ComparisonOperatorsBehavior.DefaultButDeprecated
				=> $$"""
				/// <inheritdoc cref="global::System.Numerics.IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThan(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator >({{fullTypeNameString}} left, {{fullTypeNameString}} right)
							=> {{i1}};

						/// <inheritdoc cref="global::System.Numerics.IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator <({{fullTypeNameString}} left, {{fullTypeNameString}} right)
							=> {{i2}};

						/// <inheritdoc cref="global::System.Numerics.IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThanOrEqual(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator >=({{fullTypeNameString}} left, {{fullTypeNameString}} right)
							=> {{i3}};

						/// <inheritdoc cref="global::System.Numerics.IComparisonOperators{TSelf, TOther, TResult}.op_LessThanOrEqual(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator <=({{fullTypeNameString}} left, {{fullTypeNameString}} right)
							=> {{i4}};
				""",
			ComparisonOperatorsBehavior.WithScopedIn or ComparisonOperatorsBehavior.WithScopedInButDeprecated
				=> $$"""
				/// <inheritdoc cref="global::System.Numerics.IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThan(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator >(in {{fullTypeNameString}} left, in {{fullTypeNameString}} right)
							=> {{i1}};

						/// <inheritdoc cref="global::System.Numerics.IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator <(in {{fullTypeNameString}} left, in {{fullTypeNameString}} right)
							=> {{i2}};

						/// <inheritdoc cref="global::System.Numerics.IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThanOrEqual(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator >=(in {{fullTypeNameString}} left, in {{fullTypeNameString}} right)
							=> {{i3}};

						/// <inheritdoc cref="global::System.Numerics.IComparisonOperators{TSelf, TOther, TResult}.op_LessThanOrEqual(TSelf, TOther)"/>
						{{attributesMarked}}
						[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
						[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{typeof(TypeImplHandler).FullName}}", "{{Value}}")]
						public static bool operator <=(in {{fullTypeNameString}} left, in {{fullTypeNameString}} right)
							=> {{i4}};

						{{explicitImplementation}}
				""",
			_ => null
		};
		if (operatorDeclaration is null)
		{
			return null;
		}

		return $$"""
			namespace {{namespaceString}}
			{
			#line 1 "TypeImpl.{{typeNameString}}_ComparisonOperators.g.cs"
				partial {{typeKindString}} {{typeNameString}}
				{
					{{operatorDeclaration}}
				}
			#line default
			}
			""";
	}
}

/// <summary>
/// Indicates the implementation flags.
/// </summary>
[Flags]
file enum TypeImplFlag
{
	Object_Equals = 1 << 0,
	Object_GetHashCode = 1 << 1,
	Object_ToString = 1 << 2,
	EqualityOperators = 1 << 3,
	ComparisonOperators = 1 << 4
}

/// <summary>
/// Represents a behavior for generating <see cref="object.Equals(object)"/> method.
/// </summary>
/// <seealso cref="object.Equals(object)"/>
file enum EqualsBehavior
{
	ReturnFalse,
	IsCast,
	AsCast,
	Throw,
	MakeAbstract
}

/// <summary>
/// Represents a behavior for generating <see cref="object.GetHashCode"/> method.
/// </summary>
/// <seealso cref="object.GetHashCode"/>
file enum GetHashCodeBehavior
{
	ReturnNegativeOne,
	Direct,
	EnumExplicitCast,
	Specified,
	Throw,
	HashCodeAdd,
	MakeAbstract
}

/// <summary>
/// Represents a behavior for generating <see cref="object.ToString"/> method.
/// </summary>
/// <seealso cref="object.ToString"/>
file enum ToStringBehavior
{
	Throw,
	ReturnTypeName,
	CallOverloadExplicit,
	CallOverloadImplicit,
	Specified,
	RecordLike,
	MakeAbstract
}

/// <summary>
/// Represents a behavior for generating equality operators.
/// </summary>
file enum EqualityOperatorsBehavior
{
	DoNothing,
	Default,
	DefaultButDeprecated,
	WithScopedIn,
	WithScopedInButDeprecated
}

/// <summary>
/// Represents a behavior for generating comparison operators.
/// </summary>
file enum ComparisonOperatorsBehavior
{
	DoNothing,
	Default,
	DefaultButDeprecated,
	WithScopedIn,
	WithScopedInButDeprecated,
}
